import mapboxgl, {
  type ColorSpecification,
  type DataDrivenPropertyValueSpecification,
} from 'mapbox-gl';
import type { Mapless } from '..';
import type { IdHubType } from '../core/IdHub';
import IdHub from '../core/IdHub';
import type { Handler } from '../core/Listener';
import { EmitManager } from '../core/manager';
import type { ManagerOptions, Movable, MovableManagerEvents } from '../index.d';

type MovableIdHubType = IdHubType & {
  detailLayerId: string;
  simpleLayerId: string;
  sourceId: string;
};

const stateColorRule: DataDrivenPropertyValueSpecification<ColorSpecification> =
  [
    'case',
    ['==', ['get', 'state'], 'busy'],
    'rgba(250, 173, 20, 0.8)',
    ['==', ['get', 'state'], 'fault'],
    'rgba(245, 34, 45, 0.8)',
    ['==', ['get', 'state'], 'offline'],
    'rgba(151, 151, 151, 0.8)',
    ['==', ['get', 'state'], 'disable'],
    'rgba(151, 151, 151, 0.8)',
    'rgba(82, 196, 26, 0.8)',
  ];

class MovableManager extends EmitManager<
  MovableManagerEvents,
  MovableIdHubType
> {
  private movables: Movable[] | undefined = undefined;

  constructor(mapless: Mapless, options?: ManagerOptions) {
    super(mapless, options, 'movable');
    this.onInitialized();

    this.setupClickHandler();
    this.setupHoverHandler();
    this.movables = [];
  }

  override createIdHub(_idHub?: IdHubType) {
    const newHub = new IdHub({
      layers: {
        detailLayerId: 'dzmap-movable-detail-layer',
        simpleLayerId: 'dzmap-movable-simple-layer',
      },
      sources: {
        sourceId: 'dzmap-movable-source',
      },
    }) as MovableIdHubType;
    super.createIdHub(newHub);
  }

  override initLayers() {
    if (!this.mapless) return;
    const sid = this.idHub?.sourceId;
    this.mapless.addEmptySource(sid, { dynamic: true });
    this.mapless.addLayer({
      id: this.idHub?.detailLayerId,
      type: 'symbol',
      source: sid,
      minzoom: 12,
      layout: {
        'icon-image': [
          'case',
          ['has', 'state'],
          ['concat', ['get', 'icon'], '_', ['get', 'state']],
          ['get', 'icon'],
        ],
        'icon-size': ['case', ['==', ['get', 'selected'], true], 0.4, 0.3],
        'icon-rotate': ['get', 'azimuth'], // 使用bearing字段控制旋转
        'icon-allow-overlap': true,
        'icon-ignore-placement': true,
        'icon-rotation-alignment': 'map',
        'text-field': [
          'case',
          ['==', ['get', 'selected'], true],
          ['get', 'name'],
          '',
        ],
        'text-font': ['Open Sans Regular', 'Arial Unicode MS Regular'],
        'text-size': ['case', ['==', ['get', 'selected'], true], 14, 12],
        'text-anchor': 'center',
        'text-offset': [0, 2.0],
        'text-allow-overlap': true, // 允许文字重叠
        'text-ignore-placement': true, // 忽略文字位置冲突
      },
      // TODO sdf
      paint: {
        'icon-color': '#f00',
        // 'icon-opacity': 1, // 确保图标不透明
        'icon-halo-color': '#1890ff',
        'icon-halo-width': 2,
        'text-color': stateColorRule,
        'text-halo-color': 'rgba(255, 255, 255, 0.3)',
        'text-halo-width': 1, // 阴影宽度
        'text-halo-blur': 0, // 阴影模糊度
      },
    });
    this.mapless.addLayer({
      id: this.idHub?.simpleLayerId,
      type: 'circle',
      source: sid,
      maxzoom: 12,
      paint: {
        'circle-radius': ['case', ['==', ['get', 'selected'], true], 6, 5],
        'circle-color': stateColorRule,
        'circle-stroke-width': [
          'case',
          ['==', ['get', 'selected'], true],
          2,
          1,
        ],
        'circle-stroke-color': [
          'case',
          ['==', ['get', 'selected'], true],
          '#1890ff',
          '#fff',
        ],
      },
    });
  }

  getFeature = (movable: Movable, keepSelected = false) => {
    return {
      type: 'Feature' as const,
      geometry: {
        type: 'Point' as const,
        coordinates: [movable.longitude, movable.latitude],
      },
      id: movable.id,
      properties: {
        id: movable.id,
        name: movable.name,
        icon: movable.icon,
        selected: keepSelected
          ? this._lastSelected?.id === movable.id
          : movable.selected,
        state: movable.state || undefined,
        azimuth: movable.azimuth || 0,
        visible: movable.visible,
      },
    };
  };

  getFeatures = (movables: Movable[], keepSelected = false) => {
    return movables.map((m) => this.getFeature(m, keepSelected));
  };

  drawMovables(movables: Movable[], keepSelected = true) {
    this.movables = movables;
    const features = this.getFeatures(movables, keepSelected);
    this.mapless?.setData(this.idHub?.sourceId, {
      type: 'FeatureCollection',
      features,
    });
  }

  updateMovables(movables: (Movable | Partial<Movable>)[]) {
    // 更新缓存数据
    movables.forEach((update) => {
      const index = this.movables?.findIndex((s) => s.id === update.id);
      if (this.movables && index !== undefined && index !== -1) {
        this.movables[index] = { ...this.movables[index], ...update };
      } else if (
        'id' in update &&
        'longitude' in update &&
        'latitude' in update
      ) {
        this.movables?.push(update as Movable);
      }
    });
    const features = this.getFeatures(this.movables || []);
    this.mapless?.setData(this.idHub?.sourceId, {
      type: 'FeatureCollection',
      features,
    });
  }

  _lastSelected: Movable | null = null;

  setSelected(id: string, selected: boolean) {
    if (selected && this._lastSelected) {
      this._lastSelected.selected = false;
      const source = this.mapless?.getSource(
        this.idHub?.sourceId,
      ) as mapboxgl.GeoJSONSource;
      const feature = this.getFeature(this._lastSelected);
      source.updateData(feature);
    }
    const index = this.movables?.findIndex((s) => s.id === id);
    if (this.movables && index !== undefined && index !== -1) {
      this.movables[index].selected = selected;
      // TODO 配合source的dynamic和feature的id实现局部更新
      const find = this.movables[index];
      const source = this.mapless?.getSource(
        this.idHub?.sourceId,
      ) as mapboxgl.GeoJSONSource;
      const feature = this.getFeature(find);
      source.updateData(feature);
      this._lastSelected = find;
    }
  }

  unselectAll() {
    this.movables?.forEach((s) => {
      s.selected = false;
    });
    const features = this.getFeatures(this.movables || []);
    this.mapless?.setData(this.idHub?.sourceId, {
      type: 'FeatureCollection',
      features,
    });
  }

  removeMovables(ids: string[]) {
    this.movables = this.movables?.filter((s) => !ids.includes(s.id));
    const features = this.getFeatures(this.movables || []);
    this.mapless?.setData(this.idHub?.sourceId, {
      type: 'FeatureCollection',
      features,
    });
  }

  clearMovables() {
    this.mapless?.setEmptyData(this.idHub?.sourceId);
    this.movables = [];
  }

  private setupClickHandler() {
    const clickHandler = (e: mapboxgl.MapMouseEvent) => {
      const feature = e.features?.[0];
      if (feature) {
        const curr = this.movables?.find(
          (m) => m.id === feature.properties?.id,
        );
        if (curr) {
          if (e.originalEvent.ctrlKey || e.originalEvent.metaKey) {
            // Ctrl/Command键按下时切换选中状态
            curr.selected = !curr.selected;
            const selected = this.movables?.filter((s) => s.selected);
            selected && this.emit('movablesSelected', selected);
            this._lastSelected = selected?.[0] || null;
          } else {
            // 普通点击时单选
            this.movables?.forEach((s) => {
              s.selected = false;
            });
            curr.selected = true;
            this.emit('movablesSelected', [curr]);
            this._lastSelected = curr;
          }
          const features = this.getFeatures(this.movables || []);
          this.mapless?.setData(this.idHub?.sourceId, {
            type: 'FeatureCollection',
            features,
          });
        }
      }
    };
    this._listener?.addIdList(
      'click',
      [this.idHub?.detailLayerId, this.idHub?.simpleLayerId],
      clickHandler as Handler,
    );
  }

  private setupHoverHandler() {
    const enterHnadler = (e: mapboxgl.MapMouseEvent) => {
      const feature = e.features?.[0];
      if (feature) {
        this.mapless?.setCursor('pointer');
      }
    };
    const leaveHandler = () => {
      this.mapless?.setCursor('');
    };
    this._listener?.addIdList(
      'mouseenter',
      [this.idHub?.detailLayerId, this.idHub?.simpleLayerId],
      enterHnadler as Handler,
    );
    this._listener?.addIdList(
      'mouseleave',
      [this.idHub?.detailLayerId, this.idHub?.simpleLayerId],
      leaveHandler as Handler,
    );
  }

  override fitBounds = () => {
    if (!this.map) return;
    const bounds = new mapboxgl.LngLatBounds();
    this.movables?.forEach((m) => {
      bounds.extend([m.longitude, m.latitude]);
    });
    this.map.fitBounds(bounds, { padding: 50 });
  };

  destroy(): void {
    super.destroy();
    this.movables = undefined;
  }
}

export default MovableManager;

export const attachMovable = (
  mapless: Mapless | undefined | null,
  name?: string,
) => mapless?.use(MovableManager, name);
export const getMovable = (
  mapless: Mapless | undefined | null,
  name?: string,
) => mapless?.get(MovableManager, name);
export const detachMovable = (
  mapless: Mapless | undefined | null,
  name?: string,
) => mapless?.unuse(MovableManager, name);
